package com.example.app.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.app.constants.CommonConstants;
import com.example.app.constants.ErrorConstants;
import com.example.app.constants.StateConstants;
import com.example.app.dao.PatientRepository;
import com.example.app.dto.Result;
import com.example.app.model.AppProductRecord;
import com.example.app.model.Member;
import com.example.app.model.MemberDiscount;
import com.example.app.model.Organ;
import com.example.app.model.Patient;
import com.example.app.model.Reservation;
import com.example.app.model.SystemConfig;
import com.example.app.model.User;
import com.example.app.service.AttendanceService;
import com.example.app.service.MemberDiscountService;
import com.example.app.service.MemberService;
import com.example.app.service.OrganService;
import com.example.app.service.ReservationService;
import com.example.app.service.SystemConfigService;
import com.example.app.service.UserService;
import com.example.app.utils.BusinessUtil;
import com.example.app.utils.ResultUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.beetl.sql.core.SQLManager;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by wangtao on 2018/8/24.
 */
@RestController
public class AppController {

    private Logger logger = LoggerFactory.getLogger(AppController.class);

    @Autowired
    SQLManager sqlManager;

    @Autowired
    private MemberService memberService;

    @Autowired
    private ReservationService reservationService;

    @Autowired
    private AttendanceService attendanceService;

    @Autowired
    private OrganService organService;

    @Autowired
    private MemberDiscountService memberDiscountService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private PatientRepository patientRepository;

    @Autowired
    private UserService userService;

    /**
     * 通过手机号码检索会员信息
     *
     * @param phone
     * @return
     */
    @GetMapping("/findByPhone")
    public Result findByPhone(@RequestParam("phone") String phone) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        Member member = memberService.findByPhone(phone);
        if (member == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("没有查询到会员信息");
            return result;
        }
        JSONObject object = new JSONObject();
        object.put("memberId", member.getId());
        object.put("patientId", member.getPatientId());
        result.setData(object);
        return result;
    }

    @GetMapping("/findAppointment")
    public Result findAppointment(@RequestParam("patientId") Long patientId, @RequestParam("startDate") String startDate, @RequestParam("endDate") String endDate) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        List<Reservation> list = reservationService.findByAppointment(patientId, startDate, endDate);
        if (list.isEmpty()) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("没有查到任何预约信息");
            return result;
        }
        JSONArray objects = new JSONArray();
        for (Reservation reservation : list) {
            JSONObject object = new JSONObject();
            object.put("patientName", reservation.getPatientName());
            object.put("appointTime", DateFormatUtils.format(reservation.getAppointment(), "yyyy-MM-dd HH:mm"));
            object.put("hospitalName", reservation.getOrganName());
            object.put("doctorId", reservation.getDoctorId());
            object.put("doctorName", reservation.getDoctorName());
            object.put("nurseId", reservation.getNurseId());
            object.put("nurseName", reservation.getNurseName());
            objects.add(object);
        }
        result.setData(objects);
        return result;
    }

    @GetMapping("/findById")
    public Result findById(@RequestParam("memberId") Long memberId) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        if (memberId == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("memberId不能为空");
            return result;
        }
        Member member = memberService.findOne(memberId);
        if (member == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("会员信息读取异常");
            return result;
        }
        JSONObject object = new JSONObject();
        object.put("level", member.getLevelName());
        object.put("star", member.getStar());
        object.put("balance", member.getStoreAccount());
        result.setData(object);
        return result;
    }

    @PostMapping("/doAppRecharge")
    public Result doAppRecharge(@RequestBody Map map) {
        Result result = new Result();
        if (!map.containsKey("memberId") || map.get("memberId") == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("memberId参数不存在或为空");
            return result;
        }
        if (!map.containsKey("money") || map.get("money") == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("money参数不存在或为空");
            return result;
        }
        try {
            result = memberService.doAppRecharge(map);
        } catch (Exception e) {
            // 记录日志
            logger.error("充值反馈接口异常：" + e.getMessage(), e);
            // 反馈处理结果
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("充值反馈异常");
        }
        return result;
    }

    /**
     * 小程序预约默认是复诊预约
     *
     * @return
     */
    @GetMapping("/getSchedule")
    public Result getSchedule(@RequestParam("patientId") Long patientId) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        if(patientId == null){
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("patientId不能为空");
            return result;
        }
        Patient patient = patientRepository.findOne(patientId);
        if(patient == null){
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("查询患者信息异常");
            return result;
        }
        SystemConfig config = systemConfigService.findOne(patient.getAccountId(), patient.getOrganId());
        if (config == null || config.getNurseInterval() == null) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("系统参数未配置");
            return result;
        }
        Map<String, List<String>> resultMap = new HashMap<>();
        try {
            // 护士接待时间间隔
            int interval = config.getNurseInterval();
            // 上班时间
            Date startDate = DateUtils.parseDate(config.getStartTime(), CommonConstants.DATE_FORMATS);
            // 下班时间
            Date endDate = DateUtils.parseDate(config.getEndTime(), CommonConstants.DATE_FORMATS);
            getNextInterval(interval, startDate, endDate, resultMap);
            result.setData(resultMap);
        } catch (ParseException e) {
            logger.error("getSchedule接口调用异常：" + e.getMessage(), e);
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("接口调用异常");
            return result;
        }
        return result;
    }

    /**
     * 递归获取预约时间点
     * @param interval
     * @param startDate
     * @param endDate
     * @param resultMap
     */
    public void getNextInterval(int interval, Date startDate, Date endDate, Map<String, List<String>> resultMap) {
        String hour = DateFormatUtils.format(startDate, "HH");
        if (resultMap.containsKey(hour)) {
            resultMap.get(hour).add(DateFormatUtils.format(startDate, "mm"));
        } else {
            List<String> times = new ArrayList<>();
            times.add(DateFormatUtils.format(startDate, "mm"));
            resultMap.put(hour, times);
        }
        startDate = DateUtils.addMinutes(startDate, interval);
        // 如果预约时间大于等于下班时间，不允许预约
        if (startDate.compareTo(endDate) >= 0) {
            return;
        }
        getNextInterval(interval, startDate, endDate, resultMap);
    }

    @GetMapping("/getDoctorAndNurse")
    public Result getDoctorAndNurse(@RequestParam("patientId") Long patientId, @RequestParam("datetime") String datetime) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        if (patientId == null) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("patientId不能为空");
            return result;
        }
        if (StringUtils.isBlank(datetime)) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("datetime不能为空");
            return result;
        }
        Map<String, JSONArray> resultMap = new HashMap<>(2);
        JSONArray nurseList = new JSONArray();
        JSONArray doctorList = new JSONArray();
        try {
            Patient patient = patientRepository.findOne(patientId);
            if (patient == null) {
                result.setCode(ErrorConstants.NOT_FIND);
                result.setMsg("未查询到患者信息");
                return result;
            }
            Organ organ = organService.findOneFromCache(patient.getOrganId());
            if(organ == null){
                result.setCode(ErrorConstants.NOT_FIND);
                result.setMsg("系统错误，组织机构未正确获取");
                return result;
            }
            SystemConfig config = systemConfigService.findOne(patient.getAccountId(), patient.getOrganId());
            if(config == null || config.getLeadCare() == null){
                result.setCode(ErrorConstants.PARAMETER_CONFIG_ERROR);
                result.setMsg("系统参数未配置，无法预约");
                return result;
            }
            if(config.getLeadCare() == StateConstants.SYSTEM_CONFIG_LEAD_DOCTOR){
                // 医生主导
                if(patient.getDoctorId() == null){
                    result.setCode(ErrorConstants.SUPERVISED_CARE_UNSPECIFIED);
                    result.setMsg("主管医生未指定，无法预约");
                    return result;
                }
                // 校验主管医生当天是否在岗
                boolean check = attendanceService.checkByUserId(patient.getDoctorId(), datetime);
                if(!check){
                    result.setCode(ErrorConstants.REST);
                    result.setMsg("主管医生休息，无法预约");
                    return result;
                }
                User user = userService.findOneFromCache(patient.getDoctorId());
                JSONObject doctor = new JSONObject();
                doctor.put("userId", user.getId());
                doctor.put("name", user.getUsername());
                doctorList.add(doctor);
                // 取患者所在诊所当天所有在岗护士
                List<User> list = userService.findAppointmentByJob(patient, organ, datetime, StateConstants.USER_JOB_NURSE);
                for (User u : list) {
                    JSONObject nurse = new JSONObject();
                    nurse.put("userId", u.getId());
                    nurse.put("name", u.getUsername());
                    nurseList.add(nurse);
                }
            }else if(config.getLeadCare() == StateConstants.SYSTEM_CONFIG_LEAD_NURSE){
                // 护士主导
                if(patient.getNurseId() == null){
                    result.setCode(ErrorConstants.SUPERVISED_CARE_UNSPECIFIED);
                    result.setMsg("主管护士未指定，无法预约");
                    return result;
                }
                // 校验护士是否在岗
                boolean check = attendanceService.checkByUserId(patient.getNurseId(), datetime);
                if(!check){
                    result.setCode(ErrorConstants.REST);
                    result.setMsg("主管护士休息，无法预约");
                    return result;
                }
                User user = userService.findOneFromCache(patient.getNurseId());
                JSONObject nurse = new JSONObject();
                nurse.put("userId", user.getId());
                nurse.put("name", user.getUsername());
                nurseList.add(nurse);
                // 取当天所有在岗医生
                List<User> list = userService.findAppointmentByJob(patient, organ, datetime, StateConstants.USER_JOB_DOCTOR);
                for (User u : list) {
                    JSONObject doctor = new JSONObject();
                    doctor.put("userId", u.getId());
                    doctor.put("name", u.getUsername());
                    doctorList.add(doctor);
                }
            }
            resultMap.put("nurseList", nurseList);
            resultMap.put("doctorList", doctorList);
            result.setData(resultMap);
        } catch (Exception e) {
            logger.error("getDoctorAndNurse接口调用异常：" + e.getMessage(), e);
            result.setCode(ErrorConstants.SYSTEM_ERROR);
            result.setMsg("查询异常");
        }
        return result;
    }

    @PostMapping("/generateAppointment")
    public Result generateAppointment(@RequestBody Map map) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("预约成功");
        if (!map.containsKey("apptTime") || map.get("apptTime") == null || StringUtils.isBlank(map.get("apptTime").toString())) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("apptTime参数不存在或为空");
            return result;
        }
        if (!map.containsKey("patientId") || map.get("patientId") == null || StringUtils.isBlank(map.get("patientId").toString())) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("patientId参数不存在或为空");
            return result;
        }
        if (!map.containsKey("doctorId") || map.get("doctorId") == null || StringUtils.isBlank(map.get("doctorId").toString())) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("doctorId参数不存在或为空");
            return result;
        }
        if (!map.containsKey("nurseId") || map.get("nurseId") == null || StringUtils.isBlank(map.get("nurseId").toString())) {
            result.setCode(ErrorConstants.PARAMETER_IS_EMPTY);
            result.setMsg("nurseId参数不存在或为空");
            return result;
        }
        String apptTime = map.get("apptTime").toString();
        Date appointment;
        try {
            appointment = DateUtils.parseDate(map.get("apptTime").toString(), new String[]{"yyyy-MM-dd HH:mm"});
        } catch (ParseException e) {
            result.setCode(ErrorConstants.DATE_FORMAT_ERROR);
            result.setMsg("预约时间格式不正确，yyyy-MM-dd HH:mm");
            return result;
        }
        Long patientId = Long.valueOf(map.get("patientId").toString());
        Long doctorId = Long.valueOf(map.get("doctorId").toString());
        Long nurseId = Long.valueOf(map.get("nurseId").toString());
        String remark = "";
        if(map.containsKey("remark")){
            remark = map.get("remark").toString();
        }
        Patient patient = patientRepository.findOne(patientId);
        if (patient == null) {
            result.setCode(ErrorConstants.NOT_FIND);
            result.setMsg("查询患者信息异常");
            return result;
        }
        Organ organ = organService.findOneFromCache(patient.getOrganId());
        if (organ == null) {
            result.setCode(ErrorConstants.NOT_FIND);
            result.setMsg("查询诊所信息异常");
            return result;
        }
        User doctor = userService.findOneFromCache(doctorId);
        if (doctor == null) {
            result.setCode(ErrorConstants.NOT_FIND);
            result.setMsg("查询医生信息异常");
            return result;
        }
        User nurse = userService.findOneFromCache(nurseId);
        if (nurse == null) {
            result.setCode(ErrorConstants.NOT_FIND);
            result.setMsg("查询护士信息异常");
            return result;
        }
        // 校验患者本周是否已有预约
        try {
            String d1 = BusinessUtil.getFirstOfWeek(apptTime);
            String d2 = BusinessUtil.getLastOfWeek(apptTime);
            boolean check = reservationService.checkAppointment(patientId, d1, d2);
            if(!check){
                result.setCode(ErrorConstants.REPEAT_APPOINTMENT);
                result.setMsg("患者本周已预约过");
                return result;
            }
        } catch (ParseException e) {
            result.setCode(ErrorConstants.SYSTEM_ERROR);
            result.setMsg("系统异常");
            return result;
        }
        boolean dFLag = false;
        boolean nFLag = false;
        RLock doctorLock = redissonClient.getLock(map.get("doctorId").toString());
        RLock nurseLock = redissonClient.getLock(map.get("nurseId").toString());
        try {
            dFLag = doctorLock.tryLock(60, 5, TimeUnit.SECONDS);
            nFLag = nurseLock.tryLock(60, 5, TimeUnit.SECONDS);
            if (!dFLag || !nFLag) {
                result.setCode(ErrorConstants.SYSTEM_BUSY);
                result.setMsg("系统繁忙，稍后再试");
                return result;
            }
            SystemConfig config = systemConfigService.findOne(patient.getAccountId(), patient.getOrganId());
            // 校验预约条件
            boolean check = reservationService.doCheckDoctor(doctorId, apptTime, config);
            if (!check) {
                result.setCode(ErrorConstants.DOCTOR_RESERVATION_IS_FULL);
                result.setMsg("医生当前时间预约已满");
                return result;
            }
            check = reservationService.doCheckNurse(nurseId, apptTime, config);
            if (!check) {
                result.setCode(ErrorConstants.NURSE_RESERVATION_IS_FULL);
                result.setMsg("护士当前时间预约已满");
                return result;
            }
            // 生成预约
            reservationService.doGenerateAppointment(patient, organ, doctor, nurse, appointment, remark);
            return result;
        } catch (Exception e) {
            logger.error("预约异常：" + e.getMessage(), e);
            result.setCode(ErrorConstants.SYSTEM_ERROR);
            result.setMsg("预约失败,系统异常");
        } finally {
            // 释放医生锁
            if (dFLag) {
                doctorLock.unlock();
            }
            // 释放护士锁
            if (nFLag) {
                nurseLock.unlock();
            }
        }
        return result;
    }

    @GetMapping("/getOrgans")
    public Result getOrgans() {
        JSONArray array = new JSONArray();
        List<Organ> organList = organService.findAllFromCache();
        for (Organ organ : organList) {
            JSONObject obj = new JSONObject();
            obj.put("id", organ.getId());
            obj.put("name", organ.getOrganName());
            array.add(obj);
        }
        return ResultUtil.success(array);
    }

    @PostMapping("/createHistory")
    public Result createHistory(@RequestBody @Valid AppProductRecord record, BindingResult results) {
        if (results.hasErrors()) {
            String message = results.getFieldError().getDefaultMessage();
            return ResultUtil.fail(message);
        }
        return memberService.doCreateHistory(record);
    }

    @GetMapping("/getMemberDiscount")
    public Result getMemberDiscount(@RequestParam Long memberId) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        result.setMsg("查询成功");
        Map<String, Object> map = new HashMap<>();
        Double discount = 1d;
        MemberDiscount memberDiscount = null;
        try {
            memberDiscount = memberDiscountService.findMemberDiscount(memberId);
        } catch (Exception e) {
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("服务器异常");
        }
        if (memberDiscount != null) {
            discount = memberDiscount.getDiscount();
        }
        map.put("discount", discount);
        result.setData(map);
        return result;
    }

    public static void main(String[] args) {

    }

}
